{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%pip install --upgrade modal\n",
    "%pip install ipywidgets"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import modal\n",
    "\n",
    "assert modal.__version__ > \"0.49.0\"\n",
    "modal.__version__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "app = modal.App(name=\"example-basic\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Handling standard Python functions\n",
    "\n",
    "Standard Python functions can of course be defined in a notebook and used on their own or be called within Modal functions.\n",
    "Below the `double` function is defined in pure-Python, and called once locally."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def double(x: int) -> int:\n",
    "    return x + x\n",
    "\n",
    "\n",
    "double(5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Handling Modal Functions\n",
    "\n",
    "If we wanted to run this trivial doubling function *in the cloud* we can write another function `double_with_modal` and decorate it with `@app.function` to register\n",
    "the function with the Modal app.\n",
    "\n",
    "To demonstrate that Modal functions you define in the notebook can be called by _other_ Modal functions, there's another function, `quadruple`, which uses `double` and `double_with_modal`.\n",
    "For numbers greater than 1 million, this function spins up containers that run in Modal, which is a _very_ inefficient way to multiply a number by four, but you can do it if you please!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "@app.function()\n",
    "def double_with_modal(x: int) -> int:\n",
    "    return x + x\n",
    "\n",
    "\n",
    "@app.function()\n",
    "def quadruple(x: int) -> int:\n",
    "    if x <= 1_000_000:\n",
    "        return double(x) + double(x)\n",
    "    else:\n",
    "        return double_with_modal.remote(x) + double_with_modal.remote(x)\n",
    "\n",
    "\n",
    "with app.run():\n",
    "    print(quadruple.local(100))  # running locally\n",
    "    print(quadruple.remote(100))  # run remotely\n",
    "    print(\"Doing a very inefficient remote multiplication just for fun!\")\n",
    "    result = quadruple.remote(10_000_000)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Evaluate the result created in above cell\n",
    "result"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### GPU-powered notebook cells!\n",
    "\n",
    "Thanks to Modal's remote execution capabilities, your notebook can be running on your laptop or a cheap CPU-only instance and take advantage of serverless GPU container execution. Here's the basics."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Define a Modal function with a GPU attached.\n",
    "@app.function(gpu=\"any\")\n",
    "def hello_gpu():\n",
    "    import subprocess\n",
    "\n",
    "    subprocess.run(\"nvidia-smi\", shell=True, check=True)\n",
    "    return \"hello from a remote GPU!\"\n",
    "\n",
    "\n",
    "# Start and run an ephemeral modal.App and execute the GPU-powered modal Function!\n",
    "with app.run():\n",
    "    result = hello_gpu.remote()\n",
    "    assert result == \"hello from a remote GPU!\"\n",
    "\n",
    "# After the app is finished you can continue executing other function's defined in your notebook and\n",
    "# use the results of your GPU functions!\n",
    "\"This is the remote GPU's return value: \" + result"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.14"
  },
  "vscode": {
   "interpreter": {
    "hash": "41aa4f5b72d46326b95133582f60c55f8bcca2a8619d8a82d21027f6cbc11af9"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
